﻿/*
 * Sdp.h
 *
 *  Created on: 2013年12月6日
 *      Author: zhengchuanjiang
 */

#ifndef SDP_H_
#define SDP_H_

#include "BasicType.h"
#include <string>
#include <vector>
#include <map>
#include <iosfwd>


namespace sdp
{

typedef std::vector< std::string >  StringArray;
typedef std::vector< int >  IntArray;


enum SendRecvMode
{
    kInactive = 0,
    kRecvOnly = 0x01,
    kSendonly = 0x02,
    kSendRecv = 0x03,
};



////////////////////////////////////////////////////////////////////////////

class  Attributes
{
public:
    typedef std::map< std::string, std::string >    StringMap;

public:
    Attributes();
    ~Attributes();

    Attributes(const Attributes& other);
    Attributes& operator = (const Attributes& other);

    bool operator == (const Attributes& other);
    bool operator < (const Attributes& other);

    void swap(Attributes& other);

    bool set(const std::string& key, const std::string& value=std::string());

    std::string& operator [](const char* key);

    std::string& operator [](const std::string& key);

    size_t size() const;

    bool empty() const;

    bool getAt(size_t idx, std::string& value) const;
    bool getAt(size_t idx, std::string& key, std::string& value) const;

    bool find(const std::string& key, std::string& value) const;
    bool find(const char* key, std::string& value) const;
    bool find(const char* key, int64_t& value) const;
    bool find(const char* key, int& value) const;
    bool find(const char* key, double& value) const;

    bool exists(const std::string& key) const;
    bool exists(const char* key) const;

    void clear();

    bool erase(const std::string& key);

    bool erase(const char* key);

    bool findWithValue(const std::string& value, std::string& key) const;

public:
    StringMap   m_map;

};

////////////////////////////////////////////////////////////////////////////
class  StrUtil
{
public:
    static int icompare(const std::string& src, const char* target);

    static int split(const std::string& src, char sp, std::string& head, std::string& tail);

    static int split(const std::string& src, char sp, size_t start,
            std::string& head, std::string& tail);

    static bool tail(const std::string& src, char sp, std::string& str);
    static bool head(const std::string& src, char sp, std::string& value);

    static bool tail(const std::string& src, char sp, int& value);



};

class  StringToken
{
public:
    StringToken(const std::string& src, char sp, size_t start = 0);

    bool next(std::string& section);

    bool next(int& value);
private:
    std::string m_src;
    char    m_sp;
    size_t  m_pos;

};

class  AttributeParser
{
public:
    static bool parse(const std::string& str, char eol, char equal, Attributes& attr);

};

SendRecvMode findMode(const Attributes& attr);

void removeMode(Attributes& attr);

const char* getName(SendRecvMode mode);

////////////////////////////////////////////////////////////////////////////

enum AddrType
{
    IP4 = 1,
    IP6
};


class   Connection
{
public:
    std::string  nettype;
    std::string  addrtype;
    std::string  address;
    int ttl;

    Connection():
        nettype("IN"),
        addrtype(),
        address(),
        ttl(-1)
    {
    }

    Connection(AddrType type, const std::string& addr):
        nettype("IN"),
        addrtype(),
        address(addr),
        ttl(-1)
    {
        setAddrType(type);
    }

    void setAddrType(AddrType type)
    {
        if (type == IP4)
        {
            addrtype = "IP4";
        }
        else
        {
            addrtype = "IP6";
        }
    }

    void clear()
    {
        nettype.clear();
        address.clear();
    }

    bool empty() const
    {
        return address.empty();
    }

};


class  Origin
{
public:
    std::string  username;
    std::string  session;
    std::string  version;

    Connection  addr;

    Origin():
        username(),
        session(),
        version(),
        addr()
    {
    }

    Origin(const std::string& user, const std::string& sess,
            const std::string& ver, AddrType type, const std::string& ip):
                username(user),
                session(sess),
                version(ver),
                addr(type, ip)
    {
    }

    Origin(const std::string& user, int sess,
        int ver, AddrType type, const std::string& ip);

};

class  Bandwidth
{
public:
    std::string     bwtype;
    int     bandwidth;      /// kbps

    Bandwidth():
        bwtype(),
        bandwidth()
    {
    }

    Bandwidth(const std::string& type, int bw):
        bwtype(type),
        bandwidth(bw)
    {
    }

    void clear()
    {
        bwtype.clear();
        bandwidth = 0;
    }

    bool empty() const
    {
        return bwtype.empty();
    }
};


class  Time
{
public:
    double start;
    double stop;

    Time():
        start(),
        stop()
    {
    }

    Time(double lower, double upper):
        start(lower),
        stop(upper)
    {
    }

    double getDuration() const
    {
        return stop - start;
    }

    void clear()
    {
        start = 0;
        stop = 0;
    }
};


class  EncryptionKey
{
public:
    std::string method;
    std::string key;

    EncryptionKey():
        method(),
        key()
    {
    }

    void clear()
    {
        method.clear();
        key.clear();
    }
};


class  H264Fmtp
{
public:
    int format;
    int packetization_mode;
    std::string  profile_level_id;
    std::string  sprop_parameter_sets;

    H264Fmtp():
        format(),
        packetization_mode(),
        profile_level_id(),
        sprop_parameter_sets()
    {
    }

    bool parse(const std::string& line);
    
};

class  AacFmtp
{
public:
    std::string  mode;
    int     streamType;
    int     indexDeltaLength;
    int     indexLength;
    int     sizeLength;
    std::string  config;

    AacFmtp():
        mode(),
        streamType(),
        indexDeltaLength(),
        indexLength(),
        sizeLength(),
        config()
    {
    }

    bool parse(const std::string& line);
};


class Codec
{
public:
    std::string name;
    int payload;
    int rate;
    std::string param;

    std::string encParam;

    Codec():
        payload(0),
        rate(90000)
    {
    }

    Codec(const std::string& cName, int cPayload, int cRate):
        name(cName),
        payload(cPayload),
        rate(cRate)
    {
    }
};

typedef std::vector< Codec >    CodecArray;


class  Medium
{
public:
    static const char* VIDEO;
    static const char* AUDIO;
    static const char* TEXT;
    static const char* APPLICATION;
    static const char* MESSAGE;

public:
    std::string  name;     /// "audio", "video", "text", "application", and "message",
    int     port;
    int     number;
    std::string  proto;  //transport protocol

    CodecArray codecs;
    IntArray formats;


    std::string     title;
    Connection  connection;

    Attributes  attributes;

    Bandwidth   bandwidth;
    EncryptionKey   key;

    std::string     control;

    Medium():
        name(),
        port(),
        number(),
        proto(),
        title(),
        connection(),
        attributes(),
        bandwidth(),
        key(),
        control()
    {
    }

    Medium(const std::string& aName, int aPort):
        name(aName),
        port(aPort),
        number(1),
        proto("RTP/AVP")
    {
    }

    Medium(const std::string& aName, int aPort, const std::string& aProtocol):
        name(aName),
        port(aPort),
        number(1),
        proto(aProtocol)
    {

    }

    bool isVideo() const
    {
        return (name == "video");
    }

    bool isAudio() const
    {
        return (name == "audio");
    }

    int getFormat() const
    {
        return formats[0];
    }

    SendRecvMode getMode() const
    {
        return findMode(attributes);
    }

    void setMode(SendRecvMode mode)
    {
        removeMode(attributes);
        attributes.set(getName(mode));
    }

    std::string getControl() const
    {
        std::string url;
        attributes.find("control", url);
        return url;
    }

    void addCodec(const Codec& codec)
    {
        formats.push_back(codec.payload);
        codecs.push_back(codec);
    }

    void addAttribute(const std::string& key)
    {
        attributes.set(key, std::string());
    }

    bool findCodec(const std::string& name, Codec& codec) const
    {
        for (size_t i = 0; i < codecs.size(); ++ i)
        {
            if (codecs[i].name == name)
            {
                codec = codecs[i];
                return true;
            }
        }
        return false;
    }

    size_t findCodec(const std::string& name) const;

    size_t match(const Codec& codec) const;

    size_t match(const CodecArray& arrCodec) const;

    size_t match(const Medium& medium) const;

    void clear();

};


typedef std::vector< Medium >     MediumArray;


class  SessionDescription
{
public:
    SessionDescription();
    ~SessionDescription();

    int version;
    Origin  origin;
    std::string  name;    /// session name
    std::string  info;
    std::string  uri;
    std::string  email;
    std::string  phone;

    Connection  connection;
    Bandwidth       bandwidth;

    Time    time;
    std::string repeatTime;
    std::string timeZone;
    EncryptionKey   key;

    Attributes  attributes;

    MediumArray mediums;


    bool parse(const char* str, size_t length);

    bool parse(const std::string& str);


    size_t getMediumCount() const;

    Medium& getMedium(size_t idx);

    void clearMedium();

    void addMedium(const Medium& medium);

    // tool method

    SendRecvMode getMode() const;

    void clear();

    bool getRange(double& start, double& stop);

    std::string toString() const;

    std::ostream& output(std::ostream& os) const;

    size_t findMedium(const std::string& name);

    size_t findMedium(const std::string& name, SendRecvMode mode);

    SendRecvMode getTotalMode(const std::string& name) const;


    template < class T >
    bool findAttr(const Medium& medium, const char* key, T& value)
    {
        if (medium.attributes.find(key, value))
        {
            return true;
        }
        return attributes.find(key, value);
    }

    const Connection& getConnection(const Medium& medium) const
    {
        return medium.connection.empty() ? connection : medium.connection;
    }

    const Connection& getConnection(size_t idx) const
    {
        if (idx >= mediums.size())
        {
            return connection;
        }
        return getConnection(mediums[idx]);
    }


private:
    bool parseLine(const std::string& line);

    bool parseOrigin(const std::string& line, Origin& origin);
    bool parseTime(const std::string& line, Time& timeDesc);

    bool parseAddr(const std::string& line, Connection& conn);
    bool parseBandwidth(const std::string& line, Bandwidth& bandwidth);
    bool parseKey(const std::string& line, EncryptionKey& key);

    bool parseMedia(const std::string& line);
    bool parseAttribute(const std::string& line, Attributes& attr);

    bool parseMedia(const std::string& line, Medium& mediaDesc);
    bool parseAttribute(const std::string& line, std::string& name, std::string& value);

    bool parseMediaAttribute(const std::string& line, Medium& mediaDesc);

    bool parseRtpmap(const std::string& str, Codec& codec);

    Medium& getCurMedia();

    bool hasMedium() const;

private:



};



std::ostream& operator << (std::ostream& os, const Bandwidth& bw);
std::ostream& operator << (std::ostream& os, const Origin& origin);
std::ostream& operator << (std::ostream& os, const Attributes& attr);
std::ostream& operator << (std::ostream& os, const Time& t);
std::ostream& operator << (std::ostream& os, const Connection& conn);
std::ostream& operator << (std::ostream& os, const Codec& codec);
std::ostream& operator << (std::ostream& os, const Medium& medium);
std::ostream& operator << (std::ostream& os, const SessionDescription& session);


} /* namespace net */





#endif /* SDP_H_ */
